import 'package:decimal/decimal.dart';

class CalcUtils {

  static final List<String> CALCULATE_CHAR = List.of(['+', '-', '*', '÷']);

  static CalcData appendStack(String value, List<String> expressStack, String express) {
    if (value == 'C') {
      expressStack.clear();
      express = '';
      return CalcData(expressStack, express, Decimal.zero);
    }

    if (expressStack.length == 0) {
      if (CALCULATE_CHAR.contains(value) || value == '<-' || value == '%') {
        return CalcData(expressStack, express, Decimal.zero);
      }
      if (value == '+/-') {
        expressStack.add('+/-');
      } else {
        expressStack.add(value);
      }
    } else {
      if (CALCULATE_CHAR.contains(value)) {
        if (CALCULATE_CHAR.contains(expressStack.last)) {
          expressStack..removeLast()..add(value);
        } else {
          expressStack.add(value);
        }
      } else if (value == '+/-') {
        if (expressStack.last == '+/-') {
          expressStack.removeLast();
        }
      } else if (value == '%') {
        if (!expressStack.last.contains('%')) {
          expressStack.add(expressStack.removeLast() + value);
        }
      } else if ('.' == value && expressStack.last.contains('.')) {
      } else if ('<-' == value) {
        if (express.length <= 1) {
          expressStack.clear();
        } else if (CALCULATE_CHAR.contains(expressStack.last)) {
          expressStack.removeLast();
          var last = expressStack.removeLast();
          expressStack.add(last.substring(0, last.length - 1));
        } else {
          var last = expressStack.removeLast();
          expressStack.add(last.substring(0, last.length - 1));
        }
      } else {
        if (CALCULATE_CHAR.contains(expressStack.last)) {
          expressStack.add(value);
        } else if (expressStack.last == '+/-') {
          expressStack.removeLast();
          expressStack.add('-$value');
        } else if (expressStack.last == '0' && value == '0') {
        } else if (expressStack.last.contains('.') && expressStack.last.endsWith('0') && value == '0') {
        } else {
          var last = expressStack.removeLast();
          expressStack.add(last + value);
        }
      }
    }
    express = transStackToExpress(expressStack);
    var result = calculateResult(expressStack);
    print(result);
    return CalcData(expressStack, express, result);
  }

  static String transStackToExpress(List<String> expressStack) {
    String express = '';
    for (String char in expressStack) {
      if (char == '+/-') {
        express += '-';
      } else {
        express += char;
      }
    }
    return express;
  }

  static Decimal calculateResult(List<String> expressStack) {
    List transData = prepareTransData(expressStack);
    return Decimal.parse(executeCalculate(transData));
  }

  static prepareTransData(List expressStack) {
    List preparedData = List();
    if (expressStack.length == 0) {
      return preparedData;
    }
    List nextLevel = List();
    bool calHigh = false;
    for (var i = 0; i < expressStack.length; ++i) {
      var char = expressStack[i];
      if (!CALCULATE_CHAR.contains(char)) {
        if (char.endsWith('%')) {
          char = (Decimal.parse(char.substring(0, char.length - 1))/Decimal.fromInt(100)).toString();
        }
      }
      if (i < expressStack.length - 1) {
        var operaChar = expressStack[i + 1];
        if ('+' == operaChar || '-' == operaChar) {
          if (calHigh) {
            nextLevel.add(char);
          } else {
            preparedData.add(char);
          }
          calHigh = false;
        } else if ('*' == operaChar || '÷' == operaChar) {
          if (!calHigh) {
            nextLevel = List();
            nextLevel.add(char);
            preparedData.add(nextLevel);
          } else {
            nextLevel.add(char);
          }
          calHigh = true;
        } else {
          if (calHigh) {
            nextLevel.add(char);
          } else {
            preparedData.add(char);
          }
        }
      } else {
        if (calHigh) {
          nextLevel.add(char);
        } else {
          preparedData.add(char);
        }
      }
    }

    return preparedData;
  }

  static String executeCalculate(List transData) {
    Decimal result = Decimal.zero;
    List<String> lowList = List();
    for (var data in transData) {
      if (data is List<dynamic>) {
        lowList.add(multiOrDivide(data));
      } else {
        lowList.add(data);
      }
    }
    return addOrSubtract(lowList);
  }

  static String multiOrDivide(List data) {
    if (data.length % 2 == 0) {
      data.removeLast();
    }
    Decimal innerRes = Decimal.zero;
    for (var i = 1; i <= data.length - 2; i+=2) {
      var before = i == 1 ? Decimal.parse(data[i - 1]) : innerRes;
      var char = data[i];
      var next = Decimal.parse(data[i + 1]);
      if (char == '*') {
        innerRes = before * next;
      } else {
        innerRes = before / next;
      }
    }
    return innerRes.toString();
  }

  static String addOrSubtract(List data) {
//    if (data.length % 2 == 0) {
//      data.removeLast();
//    }
    print('data ==== $data');
    Decimal innerRes = Decimal.zero;
    if (data.length == 1) {
      innerRes = Decimal.parse(data[0]);
    }
    for (var i = 1; i <= data.length - 2; i+=2) {
      var before = i == 1 ? Decimal.parse(data[i - 1]) : innerRes;
      var char = data[i];
      var next = Decimal.parse(data[i + 1]);
      print('before == $before, char == $char, next == $next');
      if (char == '+') {
        innerRes = before + next;
      } else {
        innerRes = before - next;
      }
    }
    return innerRes.toString();
  }

}

class CalcData {

  final List<String> expressStack;
  final String express;
  final Decimal result;

  CalcData(this.expressStack, this.express, this.result, );


}